package edu.hubu.talentmanagementsystem.auth;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Objects;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.GrantedAuthority;

import java.util.*;

/**
 * @author moonlan
 * date 2021/2/9 下午12:36
 * <p>
 * 一个JWT的获取以及验证的工具类
 */
@SuppressWarnings(value = "unused")
public class JwtUtils {

    /**
     * 默认的密钥，可以修改，但是能不改就不改
     */
    public static String SIGN = "GDYUzjdw*&#&@!4653";

    /**
     * 将Payload信息转化为Json字符串保存在JWT中
     */
    public static final ObjectMapper mapper = new ObjectMapper();

    /**
     * Jwt的Payload部分的Key
     */
    private static final String CLAIM_NAME = "payload";

    /**
     * 获取token字符串
     *
     * @param headers        token头部分
     * @param expirationDate token过期的日期
     * @param algorithm      token的密钥部分的加密算法
     * @param payload        主体信息部分
     * @return token字符串
     */
    public static String getToken(Map<String, Object> headers, Date expirationDate, Algorithm algorithm,
                                  Payload payload) {
        try {
            String value = mapper.writeValueAsString(payload);
            return JWT.create()
                    .withClaim(CLAIM_NAME, value)
                    .withExpiresAt(expirationDate)
                    .withHeader(headers)
                    .sign(algorithm);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取token字符串，
     * <p>
     * 头部分将默认为
     * key部分：alg
     * value部分：算法的名称
     *
     * @param expirationDate token过期的日期
     * @param algorithm      token的密钥部分的加密算法
     * @param payload        主体信息部分
     * @return token字符串
     */
    public static String getToken(Date expirationDate, Algorithm algorithm, Payload payload) {
        return getToken(Map.of("alg", algorithm.getName()), expirationDate, algorithm, payload);
    }

    /**
     * 获取token字符串，
     * <p>
     * 头部分将默认为
     * key部分：alg
     * value部分：算法的名称
     * <p>
     * 过期时间默认为7天后
     *
     * @param algorithm token的密钥部分的加密算法
     * @param payload   主体信息部分
     * @return token字符串
     */
    public static String getToken(Algorithm algorithm, Payload payload) {
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DATE, 7);
        return getToken(instance.getTime(), algorithm, payload);
    }

    /**
     * 获取token字符串，
     * <p>
     * 头部分将默认为
     * key部分：alg
     * value部分：算法的名称
     * <p>
     * 过期时间默认为7天后
     * <p>
     * 算法默认为HMAC256算法，密钥默认为本类中声明的 SIGN
     *
     * @param payload 主体信息部分
     * @return token字符串
     */
    public static String getToken(Payload payload) {
        return getToken(Algorithm.HMAC256(SIGN), payload);
    }

    /**
     * 验证字符串，
     * 如果验证成功将可以返回解码后的token工具类，用于获取token中存储的信息
     * 如果验证不成功，则会抛出JWTVerificationException异常
     *
     * @param algorithm token的密钥部分的加密算法
     * @param token     token字符串
     * @return 解码后的token工具类
     * @see com.auth0.jwt.exceptions.JWTVerificationException
     */
    public static DecodedJWT verifyToken(Algorithm algorithm, String token) {
        return JWT.require(algorithm).build().verify(token);
    }

    /**
     * 验证字符串，
     * 如果验证成功将可以返回解码后的token工具类，用于获取token中存储的信息
     * 如果验证不成功，则会抛出JWTVerificationException异常
     * <p>
     * 算法默认为HMAC256算法，密钥默认为本类中声明的 SIGN
     *
     * @param token token字符串
     * @return 解码后的token工具类
     * @see com.auth0.jwt.exceptions.JWTVerificationException
     */
    public static DecodedJWT verifyToken(String token) {
        return verifyToken(Algorithm.HMAC256(SIGN), token);
    }

    /**
     * 获取token中的payload部分信息，将返回封装好的Payload对象
     * 其中将默认对token进行验证操作
     * 如果验证成功将可以返回解码后的token工具类，用于获取token中存储的信息
     * 如果验证不成功，则会抛出JWTVerificationException异常
     *
     * @param algorithm token的密钥部分的加密算法
     * @param token     token字符串
     * @return 封装好的Payload对象
     * @see com.auth0.jwt.exceptions.JWTVerificationException
     */
    public static Payload getPayload(Algorithm algorithm, String token) {
        try {
            DecodedJWT verifyToken = verifyToken(algorithm, token);
            String payloadString = verifyToken.getClaim(CLAIM_NAME).asString();
            return mapper.readValue(payloadString, Payload.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        } catch (JWTVerificationException e) {
            throw new BadCredentialsException(e.getMessage());
        }
    }

    /**
     * 获取token中的payload部分信息，将返回封装好的Payload对象
     * 其中将默认对token进行验证操作
     * 如果验证成功将可以返回解码后的token工具类，用于获取token中存储的信息
     * 如果验证不成功，则会抛出JWTVerificationException异常
     * <p>
     * 算法默认为HMAC256算法，密钥默认为本类中声明的 SIGN
     *
     * @param token token字符串
     * @return 封装好的Payload对象
     * @see com.auth0.jwt.exceptions.JWTVerificationException
     */
    public static Payload getPayload(String token) {
        return getPayload(Algorithm.HMAC256(SIGN), token);
    }
}
